using System;
using Microsoft.SPOT;

namespace Gadgeteer.Modules.Sytech
{
    public partial class Accel3Axis
    {

        /// <summary>
        /// Enumeration for the G ranges
        /// </summary>
        public enum Mma7455gSelect
        {
            g8 = 0,
            g2 = 1,
            g4 = 2
        }

        /// <summary>
        /// Enum for the operating mode
        /// </summary>
        public enum Mma7455Mode
        {
            modeStandby = 0,
            modeMeasurement = 1,
            modeLevelDetection = 2,
            modePulseDetection = 3
        }

        /// <summary>
        /// Set the operating mode of the device
        /// </summary>
        /// <param name="Mode"><see cref="Mma7455Mode"/></param>
        /// <param name="gLevel"><see cref="Mma7455gSelect"/></param>
        public void SetMode(Mma7455Mode Mode, Mma7455gSelect gLevel)
        {
            // Mode register has Mode in bits 0 and 1, followed by gLevel in bits 2 and 3
            WriteRegister(0x16, (byte)((byte)Mode + (byte)((int)gLevel << 2)));
        }

        /// <summary>
        /// Current x axis data
        /// </summary>
        /// <returns></returns>
        public Int16 XData() { return ReadAxis(0); }

        /// <summary>
        /// Current Y Axis data
        /// </summary>
        /// <returns></returns>
        public Int16 YData() { return ReadAxis(2); }

        /// <summary>
        /// Current Z  Axis data
        /// </summary>
        /// <returns></returns>
        public Int16 ZData() { return ReadAxis(4); }

        /// <summary>
        /// Read an X, Y or Z axis data register
        /// </summary>
        /// <param name="Channel"></param>
        /// <returns></returns>
        private Int16 ReadAxis(byte Channel)
        {
            try
            {
                ushort Value = ReadReg16(Channel, ByteOrder.LittleEndian);

                // and sign extend...
                if ((Value & 0x0200) != 0)
                    Value |= 0xfc00;

                return (Int16)Value;
            }
            catch
            {
                return 0;
            }
        }

        /// <summary>
        /// Read all 3 axis
        /// 
        /// If new data available returns true.
        /// If no data available ( unlikely!) then returns false
        /// and X,Y,Z will be 0
        /// </summary>
        /// <param name="X"></param>
        /// <param name="Y"></param>
        /// <param name="Z"></param>
        /// <returns></returns>
        public bool ReadAll(ref Int16 X, ref Int16 Y, ref Int16 Z)
        {
            // wait until the status bit is set
            byte StatusReg;
             

            try
            {
                StatusReg = ReadReg8(9);
                if (this.DebugPrintEnabled)
                {
                    Debug.Print("StatusReg:" + StatusReg);
                }
            }
            catch
            {
                return false;
            }

          //  if ((StatusReg & 0x01) == 0)
            if ((StatusReg & 0x01) == 0)
                return false;

            // Read each channel
            X = ReadAxis(0);
            Y = ReadAxis(2);
            Z = ReadAxis(4);

            return true;
        }

        

        #region Register Read/Write

        /// <summary>Defines the byte ordering for the data in multi-byte register transfers</summary>
        public enum ByteOrder
        {
            /// <summary>BigEndian Mode (MSB First)</summary>
            BigEndian,
            /// <summary>LittleEndian Mode (LSB First)</summary>
            LittleEndian
        }

        private byte[] AddressPointerBuffer = new byte[1];
        private byte[] Buff1 = new byte[1];
        private byte[] Buff2 = new byte[2];
        private byte[] Buff3 = new byte[3];
        private int timeout = 2000;

        /// <summary>
        /// Read a MMA register
        /// Registers are 16 bit
        /// </summary>
        /// <param name="Address"></param>
        /// <returns></returns>
        private ushort ReadRegister(byte Address)
        {
            try { return ReadReg16(Address, ByteOrder.LittleEndian); }
            catch { return 0; }
        }

        /// <summary>
        /// Write an MMA reqister
        /// Does an 8 bit write
        /// </summary>
        /// <param name="Address"></param>
        /// <param name="Data"></param>
        private void WriteRegister(byte Address, byte Data)
        {
            try { WriteReg8(Address, Data); }
            catch { }
        }

        /// <summary>
        /// Read a 16 bit register, passing in the reg address
        /// to read.
        /// 16 bit read byte order is set using 
        /// mode 
        /// </summary>
        /// <param name="Reg"></param>
        /// <param name="Mode"></param>
        /// <returns></returns>
        protected ushort ReadReg16(byte Reg, ByteOrder Mode)
        {
           
            this.AddressPointerBuffer[0] = Reg;
            i2c.WriteRead(this.AddressPointerBuffer, Buff2,timeout);

            if (Mode == ByteOrder.BigEndian)
                return (ushort)(this.Buff2[0] << 8 | this.Buff2[1]);
            else
                return (ushort)(this.Buff2[1] << 8 | this.Buff2[0]);
           
        }

        protected void WriteReg8(byte Reg, byte Value)
        {  
            Buff2[0] = Reg;
            Buff2[1] = Value;
            i2c.Write(Buff2,timeout);        
        }

        protected byte ReadReg8(byte Reg)
        {
            this.AddressPointerBuffer[0] = Reg;

            //  if you use buff[1] - read is not Nak'd, locking up bus
            //  So we use buff[2] and actually do 2 bit reads, throw away the second
            // dont know if this is Meridian problem of MF problem
            i2c.WriteRead(this.AddressPointerBuffer, this.Buff2,timeout);
            
            return this.Buff2[0];
           
        }

        #endregion

    }
}
